#include "../source/message.hpp"
#include "../source/net.hpp"



namespace ns_router
{
    enum class Vtype
    {
        BOOL = 0,
        INTEGER,
        NUMERIC,
        STRING,
        ARRAY,
        OBJECT
    };
    class ServiceDescribe
    {
    public:
        using ptr = std::shared_ptr<ServiceDescribe>;
        using Servicecallback = std::function<void(const Json::Value&,Json::Value&)>;
        using ParaDescribe = std::pair<std::string,Vtype>;
        ServiceDescribe(std::string&& method_name,Servicecallback&& cb,std::vector<ParaDescribe>&& paras,Vtype result)
        :_method_name(std::move(method_name))
        ,_cb(std::move(cb))
        ,_paras(std::move(paras))
        ,_result(result)
        {}
        //检测参数类型
        bool Paracheck(const Json::Value& paras)
        {
            for(auto& pair : _paras)
            {
                //判断paras里的参数符不符合_paras组里的规则
                //isMember判断是否存在键为pair.first的对儿
                if(paras.isMember(pair.first) == false)
                {
                    LOG(ERROR,"请求函数时的参数校验不匹配!!!");
                    return false;
                }
                //注意这里的传参
                if(Check(paras[pair.first],pair.second)== false)
                {
                    LOG(ERROR,"请求函数时的参数类型不匹配!!!");
                    return false;
                }
            }
            return true;
        }
        std::string GetMethodname()
        {
            return _method_name;
        }
        bool Call(const Json::Value& val,Json::Value& result)
        {
            _cb(val,result);
            if(Resultcheck(result) == false)
            {
                LOG(ERROR,"服务端响应返回类型出错!!!");
                return false;
            }
            return true;
        }
    private:
        //判断返回值
        bool Resultcheck(const Json::Value res)
        {
            return Check(res,_result);
        }
        bool Check(const Json::Value& v,Vtype vtype)
        {
            switch(vtype)
            {
                case Vtype::BOOL: return v.isBool();
                case Vtype::INTEGER: return v.isIntegral();
                case Vtype::NUMERIC: return v.isNumeric();
                case Vtype::STRING: return v.isString();
                case Vtype::ARRAY: return v.isArray();
                case Vtype::OBJECT: return v.isObject();

            }
            return false;
        }
        std::string _method_name;
        Servicecallback _cb;
        std::vector<ParaDescribe> _paras;
        Vtype _result;
    };


    class SDescribeFactory
    {
    public:
        void Setmethodname(const std::string method_name)
        {
            _method_name = method_name;
        }
        void SetCb(ServiceDescribe::Servicecallback cb)
        {
            _cb = cb;        
        }
        void Setparas(const std::string& para,Vtype vtype)
        {
            _paras.push_back(ServiceDescribe::ParaDescribe(para,vtype));
        }
        void SetResult(Vtype result)
        {
            _result = result;
        }
        //用户调Build生产对象
        ServiceDescribe::ptr Build()
        {
            return std::make_shared<ServiceDescribe>(_method_name,_cb,_paras,_result);
        }

    private:
        std::string _method_name;
        ServiceDescribe::Servicecallback _cb;
        std::vector<ServiceDescribe::ParaDescribe> _paras;
        Vtype _result;
    };
    
    class DescribeManager
    {
    public:
        using ptr = std::shared_ptr<DescribeManager>;
        void Insert(const ServiceDescribe::ptr& sd)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _hashman.insert(std::make_pair(sd->GetMethodname(),sd));

        }
        ServiceDescribe::ptr Select(const std::string& method)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _hashman.find(method);
            if(it == _hashman.end())
            {
                return ServiceDescribe::ptr();
            }
            return it->second;
        }
        void Remove(const std::string& method)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _hashman.erase(method);
        }
    private:
        std::mutex _mutex;
        std::unordered_map<std::string,ServiceDescribe::ptr> _hashman;
    };

    class RpcRouter
    {
    public:
        //给dispacher提供
        void OnRpcRequest(const BaseConnection::ptr& conn,const RpcRequest::ptr& request)
        {
            //1.查询客户端的方法描述(add)，看服务端能否提供服务
            auto service = _dm->Select(request->GetMethod());
            if(service.get() == nullptr)
            {
                LOG(ERROR,"在服务端未查询到此方法!!!");
                return Response(conn,request,Json::Value(),Rcode::RCODE_NOT_FOUND_RPC);
            }
            //2.检测客户端方法参数...能否匹配服务端方法
            if(service->Paracheck(request->GetParameters()) == false)
            {
                LOG(ERROR,"客户端方法参数无法匹配!!!");
                return Response(conn,request,Json::Value(),Rcode::RCODE_INVALID_PARAMETERS);
            }
            //3.调用处理回调函数，处理客户端请求
            Json::Value result;
            if(service->Call(request->GetParameters(),result) == false)
            {
                LOG(ERROR,"服务端内部错误!!!");
                return Response(conn,request,Json::Value(),Rcode::RCODE_INSIDE_ERROR);
                
            }
            //4.把请求结果组织成响应，返回给客户端
            return Response(conn,request,Json::Value(),Rcode::RCODE_OK);

        }


        //让用户自己注册服务
        void RegisterMethod(const ServiceDescribe::ptr& sd)
        {
            _dm->Insert(sd);
        }
    private:
        void Response(const BaseConnection::ptr& conn,const RpcRequest::ptr& request,const Json::Value& result,Rcode rcode)
        {
            auto msg = MessageFactory::Createmsg<RpcResponse>();
            //服务端消息id和客户端请求保持一致
            msg->Setid(request->Getid());
            msg->SetMtype(Mtype::RPC_RES);
            msg->SetRcode(rcode);
            msg->SetResult(result);
            conn->Send(msg);
        }
        DescribeManager::ptr _dm;
    };
}